/******************************************************************************

 @File         PVRTTextureAPI.cpp

 @Title        OGLES/PVRTTextureAPI

 @Version      

 @Copyright    Copyright (c) Imagination Technologies Limited.

 @Platform     ANSI compatible

 @Description  OGLES texture loading.

******************************************************************************/

#include "PVRTTextureAPI.h"
#include "PVRTglesExt.h"
#include "PVRTTexture.h"
#include "PVRTDecompress.h"
#include "../framework/Common.h"
#include "../framework/FileSystem.h"
#include <malloc.h>
#undef max

/*****************************************************************************
** Functions
****************************************************************************/


/*!***********************************************************************
	@Function:		PVRTGetOGLESTextureFormat
	@Input:			sTextureHeader
	@Modified:		internalformat
	@Modified:		format
	@Modified:		type
	@Description:	Gets the OpenGLES equivalent values of internal format, 
					format and type for this texture header. This will return 
					any supported OpenGLES texture values, it is up to the user 
					to decide if these are valid for their current platform.
*************************************************************************/
const void PVRTGetOGLESTextureFormat(const PVRTextureHeaderV3& sTextureHeader, unsigned int& internalformat, unsigned int& format, unsigned int& type)
{	
	inf::uint64 PixelFormat = sTextureHeader.u64PixelFormat;
	EPVRTVariableType ChannelType = (EPVRTVariableType)sTextureHeader.u32ChannelType;
	
	//Initialisation. Any invalid formats will return 0 always.
	format = 0;
	type = 0;
	internalformat=0;

	//Get the last 32 bits of the pixel format.
	inf::uint64 PixelFormatPartHigh = PixelFormat&PVRTEX_PFHIGHMASK;

	//Check for a compressed format (The first 8 bytes will be 0, so the whole thing will be equal to the last 32 bits).
	if (PixelFormatPartHigh==0)
	{
		//Format and type == 0 for compressed textures.
		switch (PixelFormat)
		{
		case ePVRTPF_PVRTCI_2bpp_RGB:
			{
				internalformat=GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
				return;
			}
		case ePVRTPF_PVRTCI_2bpp_RGBA:
			{
				internalformat=GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
				return;
			}
		case ePVRTPF_PVRTCI_4bpp_RGB:
			{
				internalformat=GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
				return;
			}
		case ePVRTPF_PVRTCI_4bpp_RGBA:
			{
				internalformat=GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
				return;
			}
		default:
			return;
		}
	}
	else
	{
		switch (ChannelType)
		{
		case ePVRTVarTypeUnsignedByteNorm:
			{
				type = GL_UNSIGNED_BYTE;
				switch (PixelFormat)
				{
				case PVRTGENPIXELID4('r','g','b','a',8,8,8,8):
					{
						format = internalformat = GL_RGBA;
						return;
					}
				case PVRTGENPIXELID3('r','g','b',8,8,8):
					{
						format = internalformat = GL_RGB;
						return;
					}
				case PVRTGENPIXELID2('l','a',8,8):
					{
						format = internalformat = GL_LUMINANCE_ALPHA;
						return;
					}
				case PVRTGENPIXELID1('l',8):
					{
						format = internalformat = GL_LUMINANCE;
						return;
					}
				case PVRTGENPIXELID1('a',8):
					{
						format = internalformat = GL_ALPHA;
						return;
					}
				case PVRTGENPIXELID4('b','g','r','a',8,8,8,8):
					{
						format = internalformat = GL_BGRA;
						return;
					}
				default:
					return;
				}
				break;
			}
		case ePVRTVarTypeUnsignedShortNorm:
			{
				switch (PixelFormat)
				{
				case PVRTGENPIXELID4('r','g','b','a',4,4,4,4):
					{
						type = GL_UNSIGNED_SHORT_4_4_4_4;
						format = internalformat = GL_RGBA;
						return;
					}
				case PVRTGENPIXELID4('r','g','b','a',5,5,5,1):
					{
						type = GL_UNSIGNED_SHORT_5_5_5_1;
						format = internalformat = GL_RGBA;
						return;
					}
				case PVRTGENPIXELID3('r','g','b',5,6,5):
					{
						type = GL_UNSIGNED_SHORT_5_6_5;
						format = internalformat = GL_RGB;
						return;
					}
				default:
					return;
				}
				break;
			}
		default:
			return;
		}
	}
}

/*!***************************************************************************
@Function		PVRTTextureTile
@Modified		pOut		The tiled texture in system memory
@Input			pIn			The source texture
@Input			nRepeatCnt	Number of times to repeat the source texture
@Description	Allocates and fills, in system memory, a texture large enough
				to repeat the source texture specified number of times.
*****************************************************************************/
void PVRTTextureTile(
	PVRTextureHeaderV3			**pOut,
	const PVRTextureHeaderV3	* const pIn,
	const int					nRepeatCnt)
{
	unsigned int		nFormat = 0, nType = 0, nBPP, nSize, nElW = 0, nElH = 0, nElD = 0;
	unsigned char		*pMmSrc, *pMmDst;
	unsigned int		nLevel;
	PVRTextureHeaderV3	*psTexHeaderNew;

	assert(pIn->u32Width);
	assert(pIn->u32Width == pIn->u32Height);
	assert(nRepeatCnt > 1);

	PVRTGetOGLESTextureFormat(*pIn,nFormat,nFormat,nType);
	PVRTGetFormatMinDims(pIn->u64PixelFormat,nElW,nElH,nElD);
	
	nBPP = PVRTGetBitsPerPixel(pIn->u64PixelFormat);
	nSize = pIn->u32Width * nRepeatCnt;

	psTexHeaderNew	= PVRTTextureCreate(nSize, nSize, nElW, nElH, nBPP, true);
	*psTexHeaderNew	= *pIn;
	pMmDst	= (unsigned char*)psTexHeaderNew + sizeof(*psTexHeaderNew);
	pMmSrc	= (unsigned char*)pIn + sizeof(*pIn);

	for(nLevel = 0; ((unsigned int)1 << nLevel) < nSize; ++nLevel)
	{
		int nBlocksDstW = inf::math::max((unsigned int)1, (nSize >> nLevel) / nElW);
		int nBlocksDstH = inf::math::max((unsigned int)1, (nSize >> nLevel) / nElH);
		int nBlocksSrcW = inf::math::max((unsigned int)1, (pIn->u32Width >> nLevel) / nElW);
		int nBlocksSrcH = inf::math::max((unsigned int)1, (pIn->u32Height >> nLevel) / nElH);
		int nBlocksS	= nBPP * nElW * nElH / 8;

		PVRTTextureLoadTiled(
			pMmDst,
			nBlocksDstW,
			nBlocksDstH,
			pMmSrc,
			nBlocksSrcW,
			nBlocksSrcH,
			nBlocksS,
			(/*pIn->u64PixelFormat>=ePVRTPF_PVRTCI_2bpp_RGB &&*/ pIn->u64PixelFormat<=ePVRTPF_PVRTCI_4bpp_RGBA) ? true : false);

		pMmDst += nBlocksDstW * nBlocksDstH * nBlocksS;
		pMmSrc += nBlocksSrcW * nBlocksSrcH * nBlocksS;
	}

	psTexHeaderNew->u32Width = nSize;
	psTexHeaderNew->u32Height = nSize;
	psTexHeaderNew->u32MIPMapCount = nLevel+1;
	*pOut = psTexHeaderNew;
}

/*!***************************************************************************
 @Function		PVRTTextureLoadFromPointer
 @Input			pointer				Pointer to header-texture's structure
 @Modified		texName				the OpenGL ES texture name as returned by glBindTexture
 @Modified		psTextureHeader		Pointer to a PVR_Texture_Header struct. Modified to
									contain the header data of the returned texture Ignored if NULL.
 @Input			bAllowDecompress	Allow decompression if PVRTC is not supported in hardware.
 @Input			nLoadFromLevel		Which mip map level to start loading from (0=all)
 @Input			texPtr				If null, texture follows header, else texture is here.
 @Modified		pMetaData			If a valid map is supplied, this will return any and all 
									MetaDataBlocks stored in the texture, organised by DevFourCC
									then identifier. Supplying NULL will ignore all MetaData.
 @Return		PVR_SUCCESS on success
 @Description	Allows textures to be stored in C header files and loaded in. Can load parts of a
				mip mapped texture (i.e. skipping the highest detailed levels). In OpenGL Cube Map, each
				texture's up direction is defined as next (view direction, up direction),
				(+x,-y)(-x,-y)(+y,+z)(-y,-z)(+z,-y)(-z,-y).
				Sets the texture MIN/MAG filter to GL_LINEAR_MIPMAP_NEAREST/GL_LINEAR
				if mip maps are present, GL_LINEAR/GL_LINEAR otherwise.
*****************************************************************************/
bool PVRTTextureLoadFromPointer(	const void* pointer,
										unsigned int *const texName,
										const void *psTextureHeader,
										bool bAllowDecompress,
										const unsigned int nLoadFromLevel,
										const void * const texPtr,
										inf::HashMapGeneric<unsigned int, inf::HashMapGeneric<unsigned int, MetaDataBlock> > *pMetaData)
{
	//Compression bools
	bool bIsCompressedFormatSupported=false;
	bool bIsCompressedFormat=false;

	//Texture setup
	PVRTextureHeaderV3 sTextureHeader;
	unsigned char* pTextureData=NULL;

	//Just in case header and pointer for decompression.
	PVRTextureHeaderV3 sTextureHeaderDecomp;
	void* pDecompressedData=NULL;

	//Check if it's an old header format
	if((*(unsigned int*)pointer)!=PVRTEX3_IDENT)
	{
		//Convert the texture header to the new format.
		PVRTConvertOldTextureHeaderToV3((PVR_Texture_Header*)pointer,sTextureHeader,pMetaData);

		//Get the texture data.
		pTextureData = texPtr? (unsigned char*)texPtr:(unsigned char*)pointer+*(unsigned int*)pointer;
	}
	else
	{
		//Get the header from the main pointer.
		sTextureHeader=*(PVRTextureHeaderV3*)pointer;

		//Get the texture data.
		pTextureData = texPtr? (unsigned char*)texPtr:(unsigned char*)pointer+PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;

		//Read in all the meta data.
		unsigned int metaDataSize=0;
		while (metaDataSize<sTextureHeader.u32MetaDataSize && pMetaData!=NULL)
		{
			//Read the DevFourCC and advance the pointer offset.
			unsigned int DevFourCC=*(unsigned int*)((unsigned char*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
			metaDataSize+=sizeof(DevFourCC);

			//Read the Key and advance the pointer offset.
			unsigned int u32Key=*(unsigned int*)((unsigned char*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
			metaDataSize+=sizeof(u32Key);

			//Read the DataSize and advance the pointer offset.
			unsigned int u32DataSize = *(unsigned int*)((unsigned char*)pointer+PVRTEX3_HEADERSIZE+metaDataSize);
			metaDataSize+=sizeof(u32DataSize);

			//Get the current meta data.
			MetaDataBlock& currentMetaData = (*pMetaData)[DevFourCC][u32Key];

			//Assign the values to the meta data.
			currentMetaData.DevFOURCC=DevFourCC;
			currentMetaData.u32Key=u32Key;
			currentMetaData.u32DataSize=u32DataSize;
			
			//Check for data, if there is any, read it into the meta data.
			if(u32DataSize > 0)
			{
				//Allocate memory.
				currentMetaData.Data = new unsigned char[u32DataSize];

				//Copy the data.
				memcpy(currentMetaData.Data, ((unsigned char*)pointer+PVRTEX3_HEADERSIZE+metaDataSize), u32DataSize);

				//Advance the meta data size.
				metaDataSize+=u32DataSize;
			}
		}
	}

	//Return the PVRTextureHeader.
	if (psTextureHeader)
	{
		*(PVRTextureHeaderV3*)psTextureHeader=sTextureHeader;
	}
	
	//Setup GL Texture format values.
	GLenum eTextureFormat = 0;
	GLenum eTextureInternalFormat = 0;	// often this is the same as textureFormat, but not for BGRA8888 on the iPhone, for instance
	GLenum eTextureType = 0;

	//Get the OGLES format values.
	PVRTGetOGLESTextureFormat(sTextureHeader,eTextureInternalFormat,eTextureFormat,eTextureType);

	//Check supported texture formats.
	bool bIsPVRTCSupported = CPVRTglesExt::IsGLExtensionSupported("GL_IMG_texture_compression_pvrtc");
#ifndef TARGET_OS_IPHONE
	bool bIsBGRA8888Supported  = CPVRTglesExt::IsGLExtensionSupported("GL_IMG_texture_format_BGRA8888");
#else
	bool bIsBGRA8888Supported  = CPVRTglesExt::IsGLExtensionSupported("GL_APPLE_texture_format_BGRA8888");
#endif
		
	//Check for compressed formats
	if (eTextureFormat==0 && eTextureType==0 && eTextureInternalFormat!=0)
	{
		if (eTextureInternalFormat>=GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG && eTextureInternalFormat<=GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG)
		{
			//Check for PVRTCI support.
			if(bIsPVRTCSupported)
			{
				bIsCompressedFormatSupported = bIsCompressedFormat = true;
			}
			else
			{
				//Try to decompress the texture.
				if(bAllowDecompress)
				{
					//Output a warning.
					common->Error("PVRTTextureLoadFromPointer warning: PVRTC not supported. Converting to RGBA8888 instead.\n");

					//Modify boolean values.
					bIsCompressedFormatSupported = false;
					bIsCompressedFormat = true;

					//Check if it's 2bpp.
					bool bIs2bppPVRTC = (eTextureInternalFormat==GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG || eTextureInternalFormat==GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG);

					//Change texture format.
					eTextureFormat = eTextureInternalFormat = GL_RGBA;
					eTextureType = GL_UNSIGNED_BYTE;

					//Create a near-identical texture header for the decompressed header.
					sTextureHeaderDecomp = sTextureHeader;
					sTextureHeaderDecomp.u32ChannelType=ePVRTVarTypeUnsignedByteNorm;
					sTextureHeaderDecomp.u32ColourSpace=ePVRTCSpacelRGB;
					sTextureHeaderDecomp.u64PixelFormat=PVRTGENPIXELID4('r','g','b','a',8,8,8,8);

					//Allocate enough memory for the decompressed data. OGLES1, so only decompress one surface/face.
					pDecompressedData = malloc(PVRTGetTextureDataSize(sTextureHeaderDecomp, PVRTEX_ALLMIPLEVELS, false, false) );

					//Check the malloc.
					if (!pDecompressedData)
					{
						common->Error("PVRTTextureLoadFromPointer error: Unable to allocate memory to decompress texture.\n");
						return false;
					}

					//Get the dimensions for the current MIP level.
					unsigned int uiMIPWidth = sTextureHeaderDecomp.u32Width>>nLoadFromLevel;
					unsigned int uiMIPHeight = sTextureHeaderDecomp.u32Height>>nLoadFromLevel;

					//Setup temporary variables.
					unsigned char* pTempDecompData = (unsigned char*)pDecompressedData;
					unsigned char* pTempCompData = (unsigned char*)pTextureData;

					//Decompress all the MIP levels.
					for (unsigned int uiMIPMap=nLoadFromLevel;uiMIPMap<sTextureHeader.u32MIPMapCount;++uiMIPMap)
					{
						//Workout the current MIP data size for each texture.
						unsigned int compressedSize = PVRTGetTextureDataSize(sTextureHeader, uiMIPMap, false, false);
						unsigned int decompressedSize = PVRTGetTextureDataSize(sTextureHeaderDecomp, uiMIPMap, false, false);

						//Decompress the texture data.
						PVRTDecompressPVRTC(pTempCompData,bIs2bppPVRTC?1:0,uiMIPWidth,uiMIPHeight,pTempDecompData);

						//Work out the current MIP dimensions.
						uiMIPWidth=inf::math::max<unsigned int>(1,uiMIPWidth>>1);
						uiMIPHeight=inf::math::max<unsigned int>(1,uiMIPHeight>>1);

						//Move forward through the pointers.
						pTempDecompData+=decompressedSize;
						pTempCompData+=compressedSize;
					}
				}
				else
				{
					common->Error("PVRTTextureLoadFromPointer error: PVRTC not supported.\n");
					return false;
				}
			}
		}
	}

	//Check for BGRA support.	
	if(eTextureFormat==GL_BGRA)
	{
#ifdef TARGET_OS_IPHONE
		eTextureInternalFormat = GL_RGBA;
#endif
		if(!bIsBGRA8888Supported)
		{
#ifdef TARGET_OS_IPHONE
			common->Error("PVRTTextureLoadFromPointer failed: Unable to load GL_BGRA texture as extension GL_APPLE_texture_format_BGRA8888 is unsupported.\n");
#else
			common->Error("PVRTTextureLoadFromPointer failed: Unable to load GL_BGRA texture as extension GL_IMG_texture_format_BGRA8888 is unsupported.\n");
#endif
			return false;
		}
	}

	//Deal with unsupported texture formats
	if (eTextureInternalFormat==0)
	{
		common->Error("PVRTTextureLoadFromPointer failed: pixel type not supported.\n");
		return false;
	}

	//PVR files are never row aligned.
	glPixelStorei(GL_UNPACK_ALIGNMENT,1);

	//Generate a texture
	glGenTextures(1, texName);

	//Check if this is a cube map.
	if(sTextureHeader.u32NumFaces>1)
	{
		//Not supported in OpenGLES 1.x
		common->Error("PVRTTextureLoadFromPointer failed: cube map textures are not available in OGLES1.x.\n");
		return false;
	}

	//Check if this is a texture array.
	if(sTextureHeader.u32NumSurfaces>1)
	{
		//Not supported in OpenGLES 1.x
		common->Error("PVRTTextureLoadFromPointer failed: Texture arrays are not available in OGLES1.x.\n");
		return false;
	}

	//Bind the 2D texture
	glBindTexture(GL_TEXTURE_2D, *texName);

	if(glGetError())
	{
		common->Error("PVRTTextureLoadFromPointer failed: glBindTexture() failed.\n");
		return false;
	}

	//Initialise the current MIP size.
	unsigned int uiCurrentMIPSize=0;
	unsigned char* pTempData=NULL;

	unsigned int u32MIPWidth = sTextureHeader.u32Width>>nLoadFromLevel;
	unsigned int u32MIPHeight = sTextureHeader.u32Height>>nLoadFromLevel;

	//Check for a compressed texture.
	if (bIsCompressedFormat)
	{
		//If texture is supported
		if (bIsCompressedFormatSupported)
		{
			unsigned int au32Dimensions[3];
			PVRTGetFormatMinDims(sTextureHeader.u64PixelFormat,au32Dimensions[0],au32Dimensions[1],au32Dimensions[2]);
			//Set temporary pointer for traversal.
			pTempData=pTextureData;

			//Loop through all MIP levels.
			for (unsigned int uiMIPLevel=nLoadFromLevel; uiMIPLevel<sTextureHeader.u32MIPMapCount; ++uiMIPLevel)
			{
				//Get the current MIP size.
				uiCurrentMIPSize=PVRTGetTextureDataSize(sTextureHeader,uiMIPLevel,false,false);

				//Upload the texture
				glCompressedTexImage2D(GL_TEXTURE_2D,uiMIPLevel,eTextureInternalFormat,u32MIPWidth, u32MIPHeight, 0, uiCurrentMIPSize, pTempData);

				//Reduce the MIP Size.
				u32MIPWidth=inf::math::max<unsigned int>(1,u32MIPWidth>>1);
				u32MIPHeight=inf::math::max<unsigned int>(1,u32MIPHeight>>1);

				//Advance the pointer
				pTempData+=uiCurrentMIPSize;

				//Error check
				if(glGetError())
				{
					common->Error("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
					return false;
				}
			}
		}
		//If texture was decompressed
		else
		{
			//Set temporary pointer for traversal.
			pTempData=(unsigned char*)pDecompressedData;

			//Loop through all MIP levels.
			for (unsigned int uiMIPLevel=nLoadFromLevel; uiMIPLevel<sTextureHeaderDecomp.u32MIPMapCount; ++uiMIPLevel)
			{
				//Get the current MIP size.
				uiCurrentMIPSize=PVRTGetTextureDataSize(sTextureHeaderDecomp,uiMIPLevel,false,false);

				//Upload the texture
				glTexImage2D(GL_TEXTURE_2D,uiMIPLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);

				//Advance the pointer
				pTempData+=uiCurrentMIPSize;

				//Reduce the size of the MIP Map.
				u32MIPWidth=inf::math::max<unsigned int>(1,u32MIPWidth>>1);
				u32MIPHeight=inf::math::max<unsigned int>(1,u32MIPHeight>>1);

				//Error check
				if(glGetError())
				{
					free(pDecompressedData);
					common->Error("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
					return false;
				}
			}
			free(pDecompressedData);
		}
	}
	//Uncompressed texture
	else 
	{
		//Set temporary pointer for traversal.
		pTempData=pTextureData;

		//Loop through all MIP levels.
		for (unsigned int uiMIPLevel=nLoadFromLevel; uiMIPLevel<sTextureHeader.u32MIPMapCount; ++uiMIPLevel)
		{
			//Get the current MIP size.
			uiCurrentMIPSize=PVRTGetTextureDataSize(sTextureHeader,uiMIPLevel,false,false);

			//Upload the texture
			glTexImage2D(GL_TEXTURE_2D,uiMIPLevel,eTextureInternalFormat, u32MIPWidth, u32MIPHeight, 0, eTextureFormat, eTextureType, pTempData);

			//Reduce the size of the MIP Map.
			u32MIPWidth=inf::math::max<unsigned int>(1,u32MIPWidth>>1);
			u32MIPHeight=inf::math::max<unsigned int>(1,u32MIPHeight>>1);

			//Advance the pointer
			pTempData+=uiCurrentMIPSize;

			//Error check
			if(glGetError())
			{
				common->Error("PVRTTextureLoadFromPointer failed: glTexImage2D() failed.\n");
				return false;
			}
		}
	}
	
	//Set Minification and Magnification filters according to whether MIP maps are present.
	if(sTextureHeader.u32MIPMapCount==1)
	{
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	else
	{
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}

	return true;
}

/*!***************************************************************************
 @Function		PVRTTextureLoadFromPVR
 @Input			filename			Filename of the .PVR file to load the texture from
 @Modified		texName				the OpenGL ES texture name as returned by glBindTexture
 @Modified		psTextureHeader		Pointer to a PVR_Texture_Header struct. Modified to
									contain the header data of the returned texture Ignored if NULL.
 @Input			bAllowDecompress	Allow decompression if PVRTC is not supported in hardware.
 @Input			nLoadFromLevel		Which mip map level to start loading from (0=all)
 @Modified		pMetaData			If a valid map is supplied, this will return any and all 
									MetaDataBlocks stored in the texture, organised by DevFourCC
									then identifier. Supplying NULL will ignore all MetaData.
 @Return		PVR_SUCCESS on success
 @Description	Allows textures to be stored in binary PVR files and loaded in. Can load parts of a
				mip mapped texture (i.e. skipping the highest detailed levels).
				Sets the texture MIN/MAG filter to GL_LINEAR_MIPMAP_NEAREST/GL_LINEAR
				if mip maps are present, GL_LINEAR/GL_LINEAR otherwise.
*****************************************************************************/
bool PVRTTextureLoadFromPVR(	const char * const filename,
									unsigned int * const texName,
									const void *psTextureHeader,
									bool bAllowDecompress,
									const unsigned int nLoadFromLevel,
									inf::HashMapGeneric<unsigned int, inf::HashMapGeneric<unsigned int, MetaDataBlock> > *pMetaData)
{
    void*   fileData = NULL;
    int     readCount = fileSystem->ReadFile( filename, &fileData, NULL );
    //Check file opened successfully.
    if( readCount == RETURN_READ_WRITE_ERROR || (!fileData) ) {
        if( fileData )
            fileSystem->FreeFile( fileData );
        return false;
    }



	//Header size.
	unsigned int u32HeaderSize=0;

	//Boolean whether to byte swap the texture data or not.
	bool bSwapDataEndianness=false;

	//Texture header to check against.
	PVRTextureHeaderV3 sTextureHeader;

	//The channel type for endian swapping.
	EPVRTVariableType u32CurrentChannelType=ePVRTVarTypeUnsignedByte;

	//Check the first word of the file and see if it's equal to the current identifier (or reverse identifier)
	if(*(unsigned int*)fileData!=PVRTEX_CURR_IDENT && *(unsigned int*)fileData!=PVRTEX_CURR_IDENT_REV)
	{
		//Swap the header bytes if necessary.
        if( inf::is_big_endian() )
		{
			bSwapDataEndianness=true;
			unsigned int u32HeaderSize=inf::swap_4byte(*(unsigned int*)fileData);

			for (unsigned int i=0; i<u32HeaderSize; ++i)
			{
                inf::swap_bytes( (unsigned char*)( ( (unsigned int*)fileData )+i),sizeof(unsigned int) );
			}
		}

		//Get a pointer to the header.
		PVR_Texture_Header* sLegacyTextureHeader=(PVR_Texture_Header*)fileData;

		//Set the header size.
		u32HeaderSize=sLegacyTextureHeader->dwHeaderSize;

		//We only really need the channel type.
		inf::uint64 tempFormat;
		EPVRTColourSpace tempColourSpace;
		bool tempIsPreMult;

		//Map the enum to get the channel type.
		PVRTMapLegacyTextureEnumToNewFormat( (PVRTPixelType)( sLegacyTextureHeader->dwpfFlags&0xff),tempFormat,tempColourSpace, u32CurrentChannelType, tempIsPreMult);
	}
	// If the header file has a reverse identifier, then we need to swap endianness
	else if(*(unsigned int*)fileData==PVRTEX_CURR_IDENT_REV)
	{
		//Setup the texture header
		sTextureHeader=*(PVRTextureHeaderV3*)fileData;

		bSwapDataEndianness=true;
		PVRTextureHeaderV3* pTextureHeader=(PVRTextureHeaderV3*)fileData;

		pTextureHeader->u32ChannelType=inf::swap_4byte(pTextureHeader->u32ChannelType);
		pTextureHeader->u32ColourSpace=inf::swap_4byte(pTextureHeader->u32ColourSpace);
		pTextureHeader->u32Depth=inf::swap_4byte(pTextureHeader->u32Depth);
		pTextureHeader->u32Flags=inf::swap_4byte(pTextureHeader->u32Flags);
		pTextureHeader->u32Height=inf::swap_4byte(pTextureHeader->u32Height);
		pTextureHeader->u32MetaDataSize=inf::swap_4byte(pTextureHeader->u32MetaDataSize);
		pTextureHeader->u32MIPMapCount=inf::swap_4byte(pTextureHeader->u32MIPMapCount);
		pTextureHeader->u32NumFaces=inf::swap_4byte(pTextureHeader->u32NumFaces);
		pTextureHeader->u32NumSurfaces=inf::swap_4byte(pTextureHeader->u32NumSurfaces);
		pTextureHeader->u32Version=inf::swap_4byte(pTextureHeader->u32Version);
		pTextureHeader->u32Width=inf::swap_4byte(pTextureHeader->u32Width);
        inf::swap_bytes((unsigned char*)&pTextureHeader->u64PixelFormat,sizeof(inf::uint64));

		//Channel type.
		u32CurrentChannelType=(EPVRTVariableType)pTextureHeader->u32ChannelType;

		//Header size.
		u32HeaderSize=PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;
	}
	else
	{
		//Header size.
		u32HeaderSize=PVRTEX3_HEADERSIZE+sTextureHeader.u32MetaDataSize;
	}

	// Convert the data if needed
	if(bSwapDataEndianness)
	{
		//Get the size of the variables types.
		unsigned int ui32VariableSize=0;
		switch(u32CurrentChannelType)
		{
		case ePVRTVarTypeFloat:
		case ePVRTVarTypeUnsignedInteger:
		case ePVRTVarTypeUnsignedIntegerNorm:
		case ePVRTVarTypeSignedInteger:
		case ePVRTVarTypeSignedIntegerNorm:
			{
				ui32VariableSize=4;
				break;
			}
		case ePVRTVarTypeUnsignedShort:
		case ePVRTVarTypeUnsignedShortNorm:
		case ePVRTVarTypeSignedShort:
		case ePVRTVarTypeSignedShortNorm:
			{
				ui32VariableSize=2;
				break;
			}
		case ePVRTVarTypeUnsignedByte:
		case ePVRTVarTypeUnsignedByteNorm:
		case ePVRTVarTypeSignedByte:
		case ePVRTVarTypeSignedByteNorm:
			{
				ui32VariableSize=1;
				break;
			}
		default:
			break;
		}
		
		//If the size of the variable type is greater than 1, then we need to byte swap.
		if (ui32VariableSize>1)
		{
			//Get the texture data.
			unsigned char* pu8OrigData = ( (unsigned char*)fileData + u32HeaderSize);

			//Get the size of the texture data.
			unsigned int ui32TextureDataSize = PVRTGetTextureDataSize(sTextureHeader);
				
			//Loop through and byte swap all the data. It's swapped in place so no need to do anything special.
			for(unsigned int i = 0; i < ui32TextureDataSize; i+=ui32VariableSize)
			{
                inf::swap_bytes(pu8OrigData+i,ui32VariableSize);
			}
		}
	}
	
	int r =  PVRTTextureLoadFromPointer(fileData, texName, psTextureHeader, bAllowDecompress, nLoadFromLevel,NULL,pMetaData);
    if( fileData )
        fileSystem->FreeFile( fileData );

    return r;
}

/*!***************************************************************************
 @Function			PVRTTextureFormatGetBPP
 @Input				nFormat
 @Input				nType
 @Description		Returns the bits per pixel (BPP) of the format.
*****************************************************************************/
unsigned int PVRTTextureFormatGetBPP(const unsigned int nFormat, const unsigned int nType)
{
	switch(nFormat)
	{
	case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
	case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
		return 2;
	case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
	case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
		return 4;
	case GL_UNSIGNED_BYTE:
		switch(nType)
		{
		case GL_RGBA:
		case GL_BGRA:
			return 32;
		}
	case GL_UNSIGNED_SHORT_5_5_5_1:
		switch(nType)
		{
		case GL_RGBA:
			return 16;
		}
	}

	return 0xFFFFFFFF;
}

/*****************************************************************************
 End of file (PVRTTextureAPI.cpp)
*****************************************************************************/

